#ifndef _CRC_FUNC_DEF_H_
#define _CRC_FUNC_DEF_H_

#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define MAKE_REVERSE_FUN(bits)                           \
    uint##bits##_t reverseBits##bits(uint##bits##_t num) \
    {                                                    \
        uint##bits##_t result = 0;                       \
        for (int i = 0; i < bits; ++i)                   \
        {                                                \
            result <<= 1;                                \
            result |= (num & 1);                         \
            num >>= 1;                                   \
        }                                                \
        return result;                                   \
    }


#define MAKE_CRC_R_FUNC(bits)                                                                                                       \
    uint##bits##_t crc##bits##_r(const uint8_t *data, size_t size, uint##bits##_t poly, uint##bits##_t init, uint##bits##_t XorOut) \
    {                                                                                                                               \
        uint##bits##_t crc = reverseBits##bits(init);                                                                               \
        size_t i, j;                                                                                                                \
        uint##bits##_t result_byte = bits >> 3;                                                                                     \
        poly = reverseBits##bits(poly);                                                                                             \
        for (i = 0; i < size; i++)                                                                                                  \
        {                                                                                                                           \
            crc ^= data[i];                                                                                                         \
            for (j = 0; j < 8; j++)                                                                                                 \
            {                                                                                                                       \
                if (crc & 1u)                                                                                                       \
                {                                                                                                                   \
                    crc >>= 1;                                                                                                      \
                    crc ^= poly;                                                                                                    \
                }                                                                                                                   \
                else                                                                                                                \
                {                                                                                                                   \
                    crc >>= 1;                                                                                                      \
                }                                                                                                                   \
            }                                                                                                                       \
        }                                                                                                                           \
        return crc ^ XorOut;                                                                                                        \
    }

#define MAKE_CRC_N_FUNC(bits)                                                                                                       \
    uint##bits##_t crc##bits##_n(const uint8_t *data, size_t size, uint##bits##_t poly, uint##bits##_t init, uint##bits##_t XorOut) \
    {                                                                                                                               \
        uint##bits##_t crc = init;                                                                                                  \
        size_t i, j;                                                                                                                \
        for (i = 0; i < size; i++)                                                                                                  \
        {                                                                                                                           \
            crc ^= (uint##bits##_t)data[i] << (bits - 8);                                                                           \
            for (j = 0; j < 8; j++)                                                                                                 \
            {                                                                                                                       \
                if (crc & (uint##bits##_t)(1UL << (bits - 1)))                                                                      \
                {                                                                                                                   \
                    crc = (crc << 1) ^ poly;                                                                                        \
                }                                                                                                                   \
                else                                                                                                                \
                {                                                                                                                   \
                    crc <<= 1;                                                                                                      \
                }                                                                                                                   \
            }                                                                                                                       \
        }                                                                                                                           \
        return crc ^ XorOut;                                                                                                        \
    }

#define  MAKE_CRC_FUNC(bits) \
    MAKE_REVERSE_FUN(bits)   \
    MAKE_CRC_N_FUNC(bits)    \
    MAKE_CRC_R_FUNC(bits)    

#define MAKE_CRC_FUNC_EXTERN(bits)  \
extern uint##bits##_t crc##bits##_n(const uint8_t *data, size_t size, uint##bits##_t poly, uint##bits##_t init, uint##bits##_t XorOut); \
extern uint##bits##_t crc##bits##_r(const uint8_t *data, size_t size, uint##bits##_t poly, uint##bits##_t init, uint##bits##_t XorOut); 

#endif // !_CRC_FUNC_DEF_H_